package monarc.jscheduler.dag;

import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import java.util.ArrayList;

import monarc.job.Job;
import monarc.center.Activity;
import monarc.MainSim;

import monarc.jscheduler.JobScheduler;
import monarc.jscheduler.dag.algorithm.Algorithm;
import monarc.jscheduler.dag.graph.Muchie;
import monarc.jscheduler.dag.graph.Nod;
import monarc.jscheduler.dag.graph.PMuchie;
import monarc.jscheduler.dag.graph.Procesor;

import edu.uci.ics.jung.graph.impl.DirectedSparseGraph;
import edu.uci.ics.jung.graph.impl.UndirectedSparseGraph;
import engine.Event;
import engine.Scheduler;

/**
 * 
 * @author Alexandra Olteanu
 * 
 * Procedura de replanificare :
 * - se poate replanifica doar un singur nod 
 * - se poate replanifica o parte din graf
 * - se poate replanifica intreg graful
 * 
 */

public class ReschedulingProcedure extends DAGScheduler{
	
	
	public Integer type;
	public Nod node = null;
	public DirectedSparseGraph tasks;
	public Vector<Procesor> vProc;
	
	/** 
	 * Default constructor 
	 */
	public ReschedulingProcedure() {
		super();
	}
	
	/**
	 * This method construct the processors vector
	 * @param procesoare
	 */
	public void setProc(UndirectedSparseGraph procesoare){
		vProc = new Vector<Procesor>();		
		vProc.clear();
		//construirea vectorului cu procesoare
		Set st = procesoare.getVertices();
		Iterator iter = st.iterator();
		while ( iter.hasNext() )
			vProc.add((Procesor)iter.next());		
	}
	
	
	/**
	 * This method copy a graph
	 * @return the new graph
	 */
	public DirectedSparseGraph copyGraph(){
		DirectedSparseGraph g = new DirectedSparseGraph();
		
		Set st = tasks.getVertices();
		Iterator iter = st.iterator();
		while ( iter.hasNext() ){
			Nod n = (Nod)((Nod)iter.next()).copy(g);

		}
		st = tasks.getEdges();
		iter = st.iterator();
		while ( iter.hasNext() ){
			Muchie mc =(Muchie)((Muchie)iter.next()).copy(g);
			
		}
		
		return g;
	}
	
	/**
	 * This method finds an edge in a graph
	 * @param g the graph
	 * @param mc the edge
	 * @return the edge from the graph
	 */
	public Muchie findMuchie(DirectedSparseGraph g, Muchie mc){
		Set st = g.getEdges();
		Iterator iter = st.iterator();
		while ( iter.hasNext() ){
			Muchie m = (Muchie)iter.next();
			if(mc.equals(m)){
				return m;
			}
		}
		
		return null;
	}
	
	/**
	 * This method finds an node in a graph
	 * @param g the graph
	 * @param nod the node
	 * @return the node from the graph
	 */
	public Nod findNode(DirectedSparseGraph g, Nod nod){
		Set st = g.getVertices();
		Iterator iter = st.iterator();
		while ( iter.hasNext() ){
			Nod n = (Nod)iter.next();
			if(nod.equals(n)){
				return n;
			}
		}
		return null;
	}
	
	/**
	 * This method add a node to a list of nodes
	 * @param nodeList list of nodes
	 * @param node the node
	 */
	public void addList(ArrayList<Nod> nodeList, Nod node){
		if(!isInList(nodeList,node))
			nodeList.add(node);	
	}
	
	/**
	 * This method determine if a node is in a list of nodes
	 * @param nodeList a list of nodes
	 * @param node the node to lookup in nodeList
	 * @return true if the node is in nodeList, else false
	 */
	public boolean isInList(ArrayList nodeList, Nod node){
		Integer aux = 0;
		Iterator iter = nodeList.iterator();
		while ( iter.hasNext() ){
			Nod n = (Nod)iter.next();
			if(node.equals(n))
			{
				aux = 1;
			}
		}
		if(aux == 0)
			return false;
		
		return true;
	}
	
	/**
	 * This method construct a subgraph that have like root node the parameter node
	 * @param node the root node for the subgraph
	 * @return the subgraph
	 */
	public DirectedSparseGraph constructSubGraph(Nod node){
		DirectedSparseGraph g = new DirectedSparseGraph();
		g = copyGraph();
		Integer aux = 0;
		ArrayList<Nod> listNode = new ArrayList<Nod>();
		addList(listNode,node);
		System.out.println(g);
		
		//construiesc lista
		while(aux<listNode.size()){
			
			Nod nl = (Nod)listNode.get(aux);
			Set st = tasks.getEdges();
			Iterator iter = st.iterator();
			
			while ( iter.hasNext() ){
				Muchie mc = (Muchie)iter.next();
				Nod n = (Nod)mc.getSource() ;
				if((nl.equals(n))){
					addList(listNode,(Nod)mc.getDest());
				}	
			}
			aux++;
		}
		
		Set st = tasks.getEdges();
		Iterator iter = st.iterator();
		//sterg muchiile care nu au ambele noduri in lista
		while ( iter.hasNext() ){
			Muchie mc = (Muchie)iter.next();
			Nod ns = (Nod)mc.getSource();
			Nod nd = (Nod)mc.getDest();
			if(!(isInList(listNode,ns)&&isInList(listNode,nd))){
				Muchie m = findMuchie(g,mc);
				if(mc!=null)
					g.removeEdge(m);
			}
		}
		//sterg nodurile care nu se afla in lista
		st = tasks.getVertices();
		iter = st.iterator();
		while ( iter.hasNext() ){
			Nod n = (Nod)iter.next();
			if(!isInList(listNode,n)){
				Nod nx =findNode(g,n);
				if(nx!=null)
					g.removeVertex(nx);
			}
		}

		System.out.println(g);
		return g;
	}
	
	/**
	 * Planing a new job using an activity
	 * @param job
	 */
	public void pushJob(DAGJob job){
		Activity a= new Activity();
		a.pushJobs();
		a.RUN();									
	}
	
	/**
	 * 
	 * @param n1
	 * @return
	 */
	public int asocProc( Nod n1 )
	{
		int index = -1;
		int index_aux = -1;
		double time = -1;
		double aux = -1;
		for ( int j = 0; j < vProc.size(); j++ )
		{
			double tmp = vProc.get(j).time;				 
			Set pred = n1.getPredecessors();
			Iterator iter = pred.iterator();
			while ( iter.hasNext() )
			{
				Nod nd = (Nod)iter.next();
				if ( nd.indexProc != j)
				{
					Muchie mc = ( Muchie )nd.findEdge( n1 );
					PMuchie pm = ( PMuchie )vProc.get(j).findEdge( vProc.get( nd.indexProc ) );
					if ( tmp < nd.fin + ( mc.data * pm.data ) ) tmp = nd.fin + ( mc.data * pm.data );
				}
				else
					if ( tmp < nd.fin ) tmp = nd.fin;
			}
			if ( time == -1 ) { 
				aux = time; 
				time = tmp; 
				index_aux = index; 
				index = vProc.get(j).index; 
			}
			else if ( time > tmp ) { 
				aux = time ; 
				time = tmp; 
				index_aux = index; 
				index = vProc.get(j).index; 
			}
		}
		n1.start = aux - vProc.get( index ).time;
		n1.initTime = aux;
		return index_aux;

	}
	
	/**
	 * 
	 * @param node
	 * @return
	 */
	public int getOtherProc(Nod node){
		return asocProc(node);
	}
	
	/**
	 * This method reschedule a node on the same processor
	 * @param node
	 */
	public void resched_nod_same_proc(Nod node){
		
		DAGTask task = new DAGTask(node.indexProc, node);
		task.setCatalog(catalog);
		catalog.changeStatus(node.index, 2);
		MainSim.scheduler.send(
				0,
				ownerFarm.getID(),
				Scheduler.clock() + node.initTime,
				Event.TAG_START_JOB,
				task);
	}
	
	/**
	 * This method reschedule a node on other processor  
	 * @param node
	 */
	public void resched_nod_other_proc(Nod node){
		int cpuID ;
		
		if((cpuID = getOtherProc(node))==-1)
			cpuID = node.indexProc;
		
		DAGTask task = new DAGTask(cpuID, node);
		task.setCatalog(catalog);
		catalog.changeStatus(node.index, 2);
		System.out.println("End node!!!");
		MainSim.scheduler.send(
				0,
				ownerFarm.getID(),
				Scheduler.clock() + node.initTime,
				Event.TAG_START_JOB,
				task);
		System.out.println(catalog.taskStatus.elementAt(node.index) + ".............."+node.index);
	}
	
	/**
	 * This method reschedule a subgraph with the same algorithm like  
	 * that the schedule was done
	 * @param node
	 */
	public void resched_graph_same_alg(Nod node){
			System.out.println("Subgraf............................................"+node.index);
			DAGJob job = new DAGJob(constructSubGraph(node));
			job.setCatalog(catalog);
			MainSim.scheduler.send(
					0,
					ownerFarm.getID(),
					Scheduler.clock(), 				
					Event.TAG_START_JOB,
					job);
	}
	
	public String setRescheduleAlgoritm(){
		return Constants.reschedule_alg;
	}
	
	/**
	 * This method reschedule a subgraph with an other algorithm then the one 
	 * that the schedule was done
	 * @param node
	 */
	public void resched_graph_other_alg(Nod node){
		DAGJob job = new DAGJob(constructSubGraph(node));
		//pushJob(job);
		String reschAlgorithm = setRescheduleAlgoritm(); 
		String alg = resched_alg(reschAlgorithm);
		catalog.setMonitorAlgorithm(alg);
		job.setCatalog(catalog);
		job.reschedule = true;
		MainSim.scheduler.send(
				0,
				ownerFarm.getID(),
				Scheduler.clock(), 				
				Event.TAG_START_JOB,
				job);
	}
	
	public String resched_alg(String alg){
		((DAGScheduler)ownerFarm.jobScheduler).setRescheduleAlgoritmToUse(alg);
		return null;
	}
	
	/**
	 * This method reschedule a part of original graph
	 * @param node
	 */
	public void reschedule(Nod node){
		if(node!=null){
			System.out.println("Nodul...................................."+node.index);
			Set succ = node.getSuccessors();
			Iterator iter = succ.iterator();
			if(iter.hasNext()){
				resched_graph_same_alg(node);
			}
			else{
				System.out.println("End node!!!");
				resched_nod_same_proc(node);
			}
		}
		else
			System.out.println("Eroare: Nod NULLLLLLL!!!!!!!!!!!!!!!!!!!!!!!!!!!");
	}
	
	/**
	 * This method set the tasks catalog
	 * @param catalog
	 */
	public void setCatalog(StatusCatalog catalog){
		this.catalog = catalog;
	}
	
	/**
	 * This method set the tasks graph
	 * @param g
	 */
	public void setGraph(DirectedSparseGraph g){
		this.tasks = g;
	}
	
	/**
	 *  Overrides the default behavior of addJob 
	 */
	public void addJob(Job newJob) {
		//write here other operation that you want to do with the new Job
		super.addJob(newJob);
	}
	
}